package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.pojo.JsonResult;
import cn.tedu.charging.common.pojo.WebSocketMessage;
import cn.tedu.charging.common.pojo.message.CheckResultMessage;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.common.pojo.message.ProgressData;
import cn.tedu.charging.common.pojo.message.ProgressMessage;
import cn.tedu.charging.common.pojo.param.ProgressCostParam;
import cn.tedu.charging.common.pojo.param.UpdateGunStatusParam;
import cn.tedu.charging.common.pojo.utils.TimeConverterUtil;
import cn.tedu.charging.common.pojo.vo.ProgressCostVO;
import cn.tedu.charging.order.common.GunStatus;
import cn.tedu.charging.order.endpoints.WebsocketServerPoint;
import cn.tedu.charging.order.feign.CostFeignClient;
import cn.tedu.charging.order.feign.DeviceFeignClient;
import cn.tedu.charging.order.mapper.BillFailMapper;
import cn.tedu.charging.order.mapper.BillSuccessMapper;
import cn.tedu.charging.order.mapper.ProgressEsRepository;
import cn.tedu.charging.order.pojo.po.BillFailPO;
import cn.tedu.charging.order.pojo.po.BillSuccessPO;
import cn.tedu.charging.order.pojo.po.ProgressEsPO;
import cn.tedu.charging.order.service.ConsumerService;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;

@Service
@Slf4j
public class ConsumerServiceImpl implements ConsumerService {
    @Autowired
    private DeviceFeignClient deviceFeignClient;
    @Autowired
    private BillSuccessMapper billSuccessMapper;
    @Autowired
    private BillFailMapper billFailMapper;
    @Autowired
    private WebsocketServerPoint wsPoint;
    @Override
    public void handleCheckResult(CheckResultMessage checkResultMessage) {
        //判断是否存在 成功或者失败
        Boolean exists = checkBillExists(checkResultMessage.getBillId());
        if (exists){
            log.debug("订单已存在，订单编号：{}",checkResultMessage.getBillId());
            return;
        }
        //1.拿到消息中的result属性
        Boolean result = checkResultMessage.getResult();
        //判断result的结果
        if (result){
            //没有持久层 2.按照成功处理 写入charging_bill_success数据
            BillSuccessPO billSuccessPO = new BillSuccessPO();
            billSuccessPO.setBillId(checkResultMessage.getBillId());
            billSuccessPO.setUserId(checkResultMessage.getUserId());
            billSuccessPO.setGunId(checkResultMessage.getGunId());
            //人为复制
            billSuccessPO.setBillStatus(1);//正在充电
            billSuccessPO.setCreateTime(new Date());
            billSuccessPO.setUpdateTime(new Date());
            billSuccessPO.setChargingStartTime(new Date());
            billSuccessMapper.insert(billSuccessPO);
            //如果设备自检成功,说明马上就可以给用户充电了,这个信息我们也通知用户
            WebSocketMessage<String> message=new WebSocketMessage<>();
            message.setState(1);//0成功马上充电 1异常和失败,2余额不足,3,充电进度
            message.setMessage("正在充电");
            message.setData("您的订单马上开始充电");
            //调用pushMsg推送 一定能推成功么
            boolean pushed = wsPoint.pushMsg(checkResultMessage.getUserId(), JSON.toJSONString(message));
            if (!pushed){
                log.debug("推送失败,用户ID：{},消息：{}",checkResultMessage.getUserId(),JSON.toJSONString(message));
            }
        }else{
            //3.按照失败处理
            //没有持久层3.1 写入charging_bill_fail数据
            BillFailPO billFailPO = new BillFailPO();
            billFailPO.setBillId(checkResultMessage.getBillId());
            billFailPO.setUserId(checkResultMessage.getUserId());
            billFailPO.setGunId(checkResultMessage.getGunId());
            billFailPO.setFailDesc(checkResultMessage.getFailDesc());
            billFailPO.setCreateTime(new Date());
            billFailPO.setUpdateTime(new Date());
            billFailMapper.insert(billFailPO);
            //3.2 调用device服务修改枪状态为3是故障
            UpdateGunStatusParam updateGunStatusParam = new UpdateGunStatusParam();
            updateGunStatusParam.setGunId(checkResultMessage.getGunId());
            updateGunStatusParam.setGunStatus(GunStatus.FAULT);
            deviceFeignClient.updateGunStatus(updateGunStatusParam);
            //3.3因为用户还在等待充电呢,所以通知用户走人
            WebSocketMessage<String> message=new WebSocketMessage<>();
            message.setState(1);//0成功马上充电 1异常和失败,2余额不足,3,充电进度
            message.setMessage("设备自检失败");
            message.setData("您的订单创建失败,sorry,请换枪,送您一张优惠券http://al993JF23");
            //调用pushMsg推送 一定能推成功么
            boolean pushed = wsPoint.pushMsg(checkResultMessage.getUserId(), JSON.toJSONString(message));
            if (!pushed){
                log.debug("推送失败,用户ID：{},消息：{}",checkResultMessage.getUserId(),JSON.toJSONString(message));
            }
        }
    }

    @Override
    public void handleCheckNoRes(DelayCheckMessage message) {
        //1.查询是否存在成功或者失败订单 利用订单编号
        Boolean exists=checkBillExists(message.getBillId());
        //判断存在
        if (!exists){
            log.debug("订单不存在，订单编号：{},按照无响应失败处理",message.getBillId());
            //2.存储失败数据
            //没有持久层3.1 写入charging_bill_fail数据
            BillFailPO billFailPO = new BillFailPO();
            billFailPO.setBillId(message.getBillId());
            billFailPO.setUserId(message.getUserId());
            billFailPO.setGunId(message.getGunId());
            billFailPO.setFailDesc("设备无响应");
            billFailPO.setCreateTime(new Date());
            billFailPO.setUpdateTime(new Date());
            billFailMapper.insert(billFailPO);
            //3.通知用户 换枪 sorry
            WebSocketMessage<String> msg=new WebSocketMessage<>();
            msg.setState(1);
            msg.setMessage("设备无响应");
            msg.setData("您久等了,您的订单启动失败,请换枪充电,sorry");
            boolean pushed = wsPoint.pushMsg(message.getUserId(), JSON.toJSONString(msg));
            if (!pushed){
                log.debug("推送失败,用户ID：{},消息：{}",message.getUserId(),JSON.toJSONString(msg));
            }
            //4.调用device修改枪状态为故障
            UpdateGunStatusParam updateGunStatusParam = new UpdateGunStatusParam();
            updateGunStatusParam.setGunId(message.getGunId());
            updateGunStatusParam.setGunStatus(GunStatus.FAULT);
            deviceFeignClient.updateGunStatus(updateGunStatusParam);
        }
    }

    @Override
    public void handleChargingProgress(ProgressMessage msg) {
        log.debug("处理充电进度消息：{}",msg);
        //1.远程调用计价中心 计算价格费用和单次充电信息
        log.debug("order-consumerService-调用计价计算充电计费信息");
        ProgressCostVO progressCostVO = calculateCost(msg);//出参入参
        log.debug("order-consumerService-远程调用计价计算充电计费信息结束,结果：{}",progressCostVO);
        //2.组织充电记录详细数据持久化 写入到es,部分数据基于计价结果
        log.debug("order-consumerSerice-组织充电记录详细数据持久化 写入到es,部分数据基于计价结果");
        //消息msg 计价vo 自定义创建id
        persistProgress(msg,progressCostVO);//出参入参
        //3.组织实时充电信息对象ProgressData 推送给用户
        log.debug("order-consumerSerice-组织实时充电信息对象ProgressData 推送给用户");
        pushProgressToUser(msg,progressCostVO);//出参入参
        //判断是否充满,如果充满结束订单状态
        if (msg.getIsFull()){
            //4.更新订单状态 最少最少要更新status
            BillSuccessPO billSuccessPO = new BillSuccessPO();
            billSuccessPO.setBillStatus(2);
            //update charging_bill_success set bill_status=2 where bill_id=?
            QueryWrapper<BillSuccessPO> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("bill_id",msg.getOrderNo());
            billSuccessMapper.update(billSuccessPO,queryWrapper);
        }

    }

    private void pushProgressToUser(ProgressMessage msg, ProgressCostVO progressCostVO) {
        //1.组织progressData
        ProgressData data=new ProgressData();
        data.setChargingCapacity(progressCostVO.getChargingCapacity());
        data.setTotalCost(progressCostVO.getTotalCost());
        data.setOneElectricityCost(progressCostVO.getPowerFee());
        data.setTotalCapacity(msg.getTotalCapacity());
        Long totalTime = msg.getTotalTime();//50000000
        //1.1 小时数 totaltime/1000*60*60 去除小数点 得到小时数整数 因为不可能到1天,不在计算一天时间数据了
        data.setHours(TimeConverterUtil.getHour(totalTime).intValue());
        //1.2 分钟数 totalTime%(3600000)/(1000*60)
        data.setMinutes(TimeConverterUtil.getMinute(totalTime).intValue());
        //1.3 秒数  totalTime%(1000*60)/1000
        data.setSeconds(TimeConverterUtil.getSecond(totalTime).intValue());
        //2.封装webSocketMessage
        WebSocketMessage<ProgressData> message=new WebSocketMessage<>();
        //2.1 1 异常错误 2 余额不足 3 充电实时进度
        message.setState(3);
        message.setMessage("充电实时进度");
        message.setData(data);
        //3.调用serverPoint的push方法
        wsPoint.pushMsg(msg.getUserId(),JSON.toJSONString(message));
    }
    @Autowired
    private ProgressEsRepository progressEsRepository;
    private void persistProgress(ProgressMessage msg, ProgressCostVO progressCostVO) {
        //1.组织一个完整的ProgressEsPO
        ProgressEsPO progress=new ProgressEsPO();
        //1.1 id值唯一 uuid
        progress.setId(UUID.randomUUID().toString().replace("-",""));
        //1.2拷贝一下msg
        BeanUtils.copyProperties(msg,progress);
        //1.3拷贝一下vo
        BeanUtils.copyProperties(progressCostVO,progress);
        log.debug("组织一个完整的ProgressEsPO：{}",progress);
        //2.调用repository的save方法
        progressEsRepository.save(progress);
    }
    @Autowired
    private CostFeignClient costFeignClient;
    private ProgressCostVO calculateCost(ProgressMessage msg) {
        //1.组织参数 ProgressCostParam 所有的属性来源 都是设备同步的消息
        ProgressCostParam param=new ProgressCostParam();
        //2.拷贝所有属性
        BeanUtils.copyProperties(msg,param);
        log.debug("调用计价中心 计算充电计费信息参数：{}",param);
        //3.调用计价规则
        JsonResult<ProgressCostVO> result = costFeignClient.calculateBill(param);
        //判断调用结果,失败异常结束
        if (result.getCode()!=null&&result.getCode()!=0){
            //调用是失败的,业务异常
            log.error("调用计价中心失败,异常信息：{}",result.getMessage());
            throw new RuntimeException(result.getMessage());
        }else{
            //调用成功,返回vo结果 单次充电度数 总充电金额 单价
            log.debug("调用计价中心成功,结果：{}",result.getData());
            return result.getData();
        }
    }

    private Boolean checkBillExists(String billId) {
        //查询成功数字 查询失败的数字
        //select count(0) from success where bill_id=?
        //select count(0) from fail where bill_id=?
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("bill_id",billId);
        Long successCount = billSuccessMapper.selectCount(queryWrapper);
        Long failCount = billFailMapper.selectCount(queryWrapper);
        return successCount>0||failCount>0;
    }
}
